package com.fuyao.cloud.personnel.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyao.cloud.admin.api.feign.RemoteUserInfoService;
import com.fuyao.cloud.common.core.util.R;
import com.fuyao.cloud.common.excel.vo.ErrorMessage;
import com.fuyao.cloud.common.security.service.CloudUser;
import com.fuyao.cloud.common.security.util.SecurityUtils;
import com.fuyao.cloud.personnel.api.constant.SalaryConstant;
import com.fuyao.cloud.personnel.api.dto.SalaryTreeDTO;
import com.fuyao.cloud.personnel.api.entity.Columncontrol;
import com.fuyao.cloud.personnel.api.entity.Salary;
import com.fuyao.cloud.personnel.api.entity.Salarysms;
import com.fuyao.cloud.personnel.mapper.ColumncontrolMapper;
import com.fuyao.cloud.personnel.mapper.SalaryMapper;
import com.fuyao.cloud.personnel.mapper.SalarysmsMapper;
import com.fuyao.cloud.personnel.service.SalaryService;
import com.fuyao.cloud.sms.api.constant.SmsConstant;
import com.fuyao.cloud.sms.api.feign.RemoteSmsService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 工资记录表
 *
 * @author 碎月
 * @date 2021-12-16 10:35:16
 */
@Service
public class SalaryServiceImpl extends ServiceImpl<SalaryMapper, Salary> implements SalaryService {
    @Autowired
    private ColumncontrolMapper columncontrolMapper;

    @Autowired
    private SalarysmsMapper salarysmsMapper;

    @Resource
    private RemoteUserInfoService userInfoService;

    @Resource
    private RemoteSmsService remoteSmsService;


    @Override
    public R importSalary(List<Map<Integer, String>> excelList, BindingResult bindingResult, String yearMonth) {
        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        Set<String> errorMsg = new HashSet<>();
        // 当前年份
        String year = String.valueOf(DateUtil.year(DateUtil.date()));
        // 当前月份
        String month = String.valueOf(DateUtil.month(DateUtil.date()) + 1);
        if (yearMonth != "" && !"null".equals(yearMonth)) {
            year = yearMonth.split("-")[0];
            month = yearMonth.split("-")[1];
        }

        List<String> actualField = new ArrayList<>();
        if (excelList != null && excelList.size() != 0) {
            R errorHeadMessage = getOrAddTableHead(excelList, errorMessageList, year, actualField);
            if (errorHeadMessage != null) {
                return errorHeadMessage;
            }
            // 找到必须要的 -- 人员编号 索引
            Integer personNumIndex = -1;
            Map<Integer, String> excelHead = excelList.get(0);
            for (Map.Entry<Integer, String> entry : excelHead.entrySet()) {
                if (entry.getValue().equals("人员编号")) {
                    personNumIndex = entry.getKey();
                    break;
                }
            }
            Integer nameIndex = -1;
            for (Map.Entry<Integer, String> entry : excelHead.entrySet()) {
                if (entry.getValue().equals("姓名")) {
                    nameIndex = entry.getKey();
                    break;
                }
            }
            if (personNumIndex == -1) {
                errorMsg.add("导入列表种不存在人员编号");
            }
            CloudUser user = SecurityUtils.getUser();
            // 每次导入的唯一编码 主要记录谁导入，什么时候导入
            String uuid = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER) + "-" + user.getUsername() + "-" + user.getId();
            // 获取所有数据
            for (int i = 1; i < excelList.size(); i++) {
                Salary salary = new Salary();
                salary.setMonth(month);
                salary.setYear(year);
                salary.setImportSerialNum(uuid);
                Map<String, String> fieldValue = new HashMap<>(32);
                int index = 0;
                for (Map.Entry<Integer, String> entry : excelList.get(i).entrySet()) {
                    if (personNumIndex.equals(entry.getKey())) {
                        salary.setUserNumber(entry.getValue());
                        continue;
                    }
                    if (nameIndex.equals(entry.getKey())) {
                        salary.setUserName(entry.getValue());
                    }
                    if (entry.getValue() == null || entry.getValue().isEmpty()) {
                        fieldValue.put(actualField.get(index++), "");
                    } else {
                        fieldValue.put(actualField.get(index++), entry.getValue());
                    }
                }
                saveSalary(actualField, fieldValue, salary);
            }
        } else {
            errorMsg.add("没有数据");
            errorMessageList.add(new ErrorMessage((long) (0), errorMsg));
        }
        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        return R.ok();
    }

    @Override
    public R sendMassSms(Integer year, Integer month) {
        List<Salary> salaryList = this.baseMapper.selectList(Wrappers.<Salary>lambdaQuery().eq(Salary::getYear, year)
                .and(salary -> salary.eq(Salary::getMonth, month)));
        List<String> userNumbers = new ArrayList<>();
        Iterator<Salary> iterator =  salaryList.iterator();
        while (iterator.hasNext()){
            Salary salary = iterator.next();
            // 该条数据没有用户编号
            if (!StringUtils.hasText(salary.getUserNumber())) {
                salary.setSendSmsStatus(SalaryConstant.SERIAL_NUM_ERR);
                this.baseMapper.updateById(salary);
                iterator.remove();
                continue;
            }
            userNumbers.add(salary.getUserNumber());
        }
        return sendSms(userNumbers, salaryList);
    }

    @Override
    public R sendUserNumbersSms(List<String> userNumbers, Integer year, Integer month) {
        if (userNumbers == null || userNumbers.size() == 0) {
            return R.failed("用户编码不能为空");
        }
        List<Salary> salaryList = this.baseMapper.selectList(Wrappers.<Salary>lambdaQuery()
                .in(Salary::getUserNumber, userNumbers)
                .and(salary -> salary.eq(Salary::getMonth, month))
                .and(salary -> salary.eq(Salary::getYear,year)));
        return sendSms(userNumbers, salaryList);
    }


    @Override
    public R sendOneSms(String userNumber,Integer year, Integer month) {
        List<String> userNumbers = new ArrayList<>();
        userNumbers.add(userNumber);
        return sendUserNumbersSms(userNumbers,year,month);
    }

    @Override
    public R sendSalaryIdSms(List<Long> salaryId) {
        List<Salary> salaryList = this.baseMapper.selectBatchIds(salaryId);
        List<String> userNumberList = salaryList.stream().map(Salary::getUserNumber).collect(Collectors.toList());
        return sendSms(userNumberList,salaryList);
    }

    @Override
    public List<Tree<String>> selectTree() {
        // 查询年份
        List<String> years = this.baseMapper.getAllYear();
        // 查询所有月份
        List<SalaryTreeDTO> yearMonths = this.baseMapper.getAllYearMonth();
        AtomicReference<Integer> sort = new AtomicReference<>(0);
        List<TreeNode<String>> collect =new ArrayList<>();

        TreeNode<String> parentNode = new TreeNode();
        parentNode.setId("所有数据");
        parentNode.setWeight(sort.getAndSet(sort.get() + 1));
        parentNode.setParentId("所有");
        parentNode.setName("所有数据");
        Map<String, Object> parentExtra = new HashMap<>(2);
        parentExtra.put("type","all");
        parentNode.setExtra(parentExtra);
        collect.add(parentNode);

        List<TreeNode<String>> yearCollect = years.stream().sorted().map(year -> {
                    TreeNode<String> treeNode = new TreeNode();
                    treeNode.setId(year);
                    treeNode.setParentId("所有");
                    treeNode.setName(year);
                    treeNode.setWeight(sort.getAndSet(sort.get() + 1));
                    Map<String, Object> extra = new HashMap<>(2);
                    extra.put("type","year");
                    treeNode.setExtra(extra);
                    return treeNode;
                }).collect(Collectors.toList());

        List<TreeNode<String>> monthCollect = yearMonths.stream()
                .sorted(Comparator.comparing(SalaryTreeDTO::getMonth)).map(yearMonth -> {
                    TreeNode<String> treeNode = new TreeNode();
                    treeNode.setId(yearMonth.getYear()+yearMonth.getMonth());
                    treeNode.setParentId(yearMonth.getYear());
                    treeNode.setName(yearMonth.getMonth());
                    Map<String, Object> extra = new HashMap<>(2);
                    extra.put("type","month");
                    treeNode.setExtra(extra);
                    return treeNode;
                }).collect(Collectors.toList());

        collect.add(parentNode);
        collect.addAll(yearCollect);
        collect.addAll(monthCollect);

        return TreeUtil.build(collect, "所有");
    }

    private R sendSms(List<String> userNumbers, List<Salary> salaryList) {
        Map<String, String> mobiles = userInfoService.getMobiles(userNumbers).getData();
        if (mobiles == null || mobiles.size() == 0) {
            return R.failed("用户编号不存在");
        }
        StringBuilder faildMsg = new StringBuilder();
        int faildCount = 0;
        LocalDateTime sendDateTime = LocalDateTime.now();
        List<Columncontrol> columncontrol = new ArrayList<>();
        for (int i = 0; i < salaryList.size(); i++) {
            Salary salary = salaryList.get(i);
            if (!mobiles.containsKey(salary.getUserNumber())){
                salary.setSendSmsStatus(SalaryConstant.USER_NOT_EXISTS);
                this.baseMapper.updateById(salary);
                continue;
            }
            String mobile = mobiles.get(salary.getUserNumber());
            if (!StringUtils.hasText(mobile)) {
                salary.setSendSmsStatus(SalaryConstant.PHONE_NUMBER_ERR);
                this.baseMapper.updateById(salary);
                continue;
            }
            if (columncontrol.isEmpty()) {
                columncontrol = this.columncontrolMapper.selectList(Wrappers.<Columncontrol>lambdaQuery().eq(Columncontrol::getYear, salary.getYear()));
            }
            // 发送短信内容
            StringBuilder content = new StringBuilder();
            content.append(salary.getYear()).append("年").append(salary.getMonth()).append("月工资信息\n");
            // 拼接其它字段
            getSalaryFiledData(salary, content, columncontrol);
            Integer res = remoteSmsService.sendSms(mobile, content.toString()).getData();
            if (res != 0) {
                faildMsg.append(salary.getUserName() + "发送失败;");
                faildCount++;
                salary.setSendSmsStatus(SalaryConstant.DID_NOT_SEND);
                continue;
            } else {
                salary.setSendSmsCount((short) (salary.getSendSmsCount() + 1));
                salary.setSendSmsStatus(SalaryConstant.SEND_SUCCESS);
                // 保存发送记录
                callbackSaveSms(mobile, salary, sendDateTime);
            }
            this.baseMapper.updateById(salary);
        }
        if (faildCount == salaryList.size()) {
            R.failed("发送失败请联系管理员");
        } else if (faildCount < salaryList.size()) {
            R.failed(faildMsg.toString());
        }
        return R.ok(SmsConstant.IMAPI_SUCC_MSG);
    }

    /**
     * 获取或添加表头信息
     *
     * @param excelList        所有数据
     * @param errorMessageList 错误信息
     * @param year             年份
     * @param actualField      真实字段
     * @return
     */
    private R getOrAddTableHead(List<Map<Integer, String>> excelList, List<ErrorMessage> errorMessageList, String year, List<String> actualField) {
        // 获取列的总数
        List<Columncontrol> columncontrolList = columncontrolMapper.selectList(Wrappers.query());
        int countColum = columncontrolList.size();
        // 获取所有的属性名称
        List<String> fields = columncontrolList.stream().map(column -> column.getFieldname()).collect(Collectors.toList());
        // 获取所有的列名称
        List<String> columns = columncontrolList.stream().map(column -> column.getColumnname()).collect(Collectors.toList());
        AtomicReference<Integer> colIndex = new AtomicReference<>(0);
        Map<String, Integer> colIndexMap = columncontrolList.stream().collect(Collectors.toMap(Columncontrol::getColumnname,
                user -> (colIndex.getAndSet(colIndex.get() + 1)),
                (oldV, newV) -> newV));
        // 错误详细信息
        Set<String> errorMsg = new HashSet<>();
        // 先获取表头数据
        Map<Integer, String> headColumn = excelList.get(0);
        for (Map.Entry<Integer, String> entry : headColumn.entrySet()) {
            // 必须存在的字段，跳过
            if (entry.getValue().equals("人员编号")) {
                continue;
            }
            // 没有当前列名
            if (!columns.contains(entry.getValue())) {
                // 找到没有用过的扩展字段（不超过一百个）
                ++countColum;
                if (!fields.isEmpty()) {
                    while (fields.contains("FIELD" + countColum)) {
                        ++countColum;
                    }
                }
                if (countColum > 100) {
                    errorMsg.add("请联系管理员，超过一百个扩展字段");
                    errorMessageList.add(new ErrorMessage((long) (0), errorMsg));
                    return R.failed(errorMessageList);
                }
                columns.add(entry.getValue());
                fields.add("FILED" + countColum);
                Columncontrol columncontrol = new Columncontrol();
                columncontrol.setYear(year);
                columncontrol.setColumnname(entry.getValue());
                columncontrol.setFieldname("FIELD" + countColum);
                columncontrol.setTablename("fy_personnel_salary");
                // 保存新列
                columncontrolMapper.insert(columncontrol);
                actualField.add("FIELD" + countColum);
            } else {
                actualField.add(fields.get(colIndexMap.get(entry.getValue())));
            }
        }
        return null;
    }

    /**
     * 保存导入的信息
     *
     * @param fields
     * @param fieldValue
     * @param salary
     */
    private void saveSalary(List<String> fields, Map<String, String> fieldValue, Salary salary) {
        Class clazz = salary.getClass();
        Field[] fieldArr = clazz.getDeclaredFields();
        for (int i = 0; i < fieldArr.length; i++) {
            String fieldName = fieldArr[i].getName().toUpperCase();
            if (fields.contains(fieldName)) {
                try {
                    fieldArr[i].setAccessible(true);
                    fieldArr[i].set(salary, fieldValue.get(fieldName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        this.baseMapper.insert(salary);
    }

    private void callbackSaveSms(String mobile, Salary salary, LocalDateTime sendDateTime) {
        Salarysms salarysms = new Salarysms();
        BeanUtils.copyProperties(salary, salarysms);
        salarysms.setSalaryId(salary.getId());
        salarysms.setStatus(SmsConstant.IMAPI_SUCC_MSG);
        salarysms.setSenddatetime(sendDateTime);
        this.salarysmsMapper.insert(salarysms);
    }

    private void getSalaryFiledData(Salary salary, StringBuilder content, List<Columncontrol> columncontrol) {
        for (int i = 0; i < columncontrol.size(); i++) {
            String columName = columncontrol.get(i).getColumnname();
            String fieldName = columncontrol.get(i).getFieldname();
            String getFieldName = "get" + fieldName.substring(0, 1) + fieldName.substring(1).toLowerCase();
            String filedValue = null;
            try {
                try {
                    Method method = salary.getClass().getMethod(getFieldName);
                    filedValue = String.valueOf(method.invoke(salary));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            if (filedValue == "null"){
                continue;
            }
            if (filedValue.equals("0") || filedValue.equals("0.00")){
                continue;
            }
            content.append(columName)
                    .append(":")
                    .append(filedValue + "\n");
        }
    }
}
